﻿#include "FITKAcousticsVTKGraphObjectPictureBase.h"

// C++
#include <math.h>

// VTK
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkCamera.h>
#include <vtkMatrix3x3.h>
#include <vtkMath.h>

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActorImage.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectPictureBase::FITKAcousticsVTKGraphObjectPictureBase(Core::FITKAbstractDataObject* dataObj)
        : FITKAcousticsVTKGraphObject3D(dataObj)
    {
        // Initialize the default render layer.
        setRenderLayer(0, 2);

        // Disable boundary calculation.
        m_hasFixedBounds = false;

        // Save the camera sensitivity.
        m_cameraSen = true;
    }

    FITKAcousticsVTKGraphObjectPictureBase::~FITKAcousticsVTKGraphObjectPictureBase()
    {
        // Delete the picture data.
        deleteVtkObj(m_picData);
        deleteVtkObj(m_picDataHighlight);
        deleteVtkObj(m_picDataDisable);
        deleteVtkObj(m_picDataHighlightDisable);
    }

    void FITKAcousticsVTKGraphObjectPictureBase::init()
    {
        // Create the picture data.
        m_picData = vtkImageData::New();
        m_picDataHighlight = vtkImageData::New();
        m_picDataDisable = vtkImageData::New();
        m_picDataHighlightDisable = vtkImageData::New();
    }

    void FITKAcousticsVTKGraphObjectPictureBase::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        // Update the visibility.
        updateVisibility();

        // Update by the camera.
        updateByCamera();
    }

    void FITKAcousticsVTKGraphObjectPictureBase::highlight(FITKAcousticsVTKCommons::ShapeType type, QColor color)
    {
        if (!m_picDataHighlight || !m_picDataHighlightDisable || !m_fActorImg)
        {
            return;
        }

        // Update picture.
        bool enabled = getDataEnabled();

        if (enabled)
        {
            m_fActorImg->SetInputData(m_picDataHighlight);
        }
        else
        {
            m_fActorImg->SetInputData(m_picDataHighlightDisable);
        }

        // Try to update the image direction and size.
        updateByCamera();

        // Save the highlight flag.
        this->Superclass::highlight(type, color);
    }

    void FITKAcousticsVTKGraphObjectPictureBase::disHighlight()
    {
        if (!m_picData || !m_picDataDisable || !m_fActorImg)
        {
            return;
        }

        // Update picture.
        bool enabled = getDataEnabled();

        if (enabled)
        {
            m_fActorImg->SetInputData(m_picData);
        }
        else
        {
            m_fActorImg->SetInputData(m_picDataDisable);
        }

        // Try to update the image direction and size.
        updateByCamera();

        // Save the highlight flag.
        this->Superclass::disHighlight();
    }

    void FITKAcousticsVTKGraphObjectPictureBase::updateByCamera()
    {
        if (!m_fRender || !m_pos3 || !m_fActorImg)
        {
            return;
        }

        vtkRenderer* renderer = m_fRender->getRenderer();
        if (!renderer)
        {
            return;
        }

        // Get the render window and camera.
        vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
        vtkCamera* camera = renderer->GetActiveCamera();
        if (!renderWindow || !camera)
        {
            return;
        }

        // Get the input image data.
        vtkImageData* imageData = m_fActorImg->GetInput();
        if (!imageData)
        {
            return;
        }

        // Change the direction of the image.
        //@{
        // Get the camera direction.
        double* cameraDir = camera->GetDirectionOfProjection();
        vtkMath::Normalize(cameraDir);

        // Get the camera up direction.
        double* cameraUpDir = camera->GetViewUp();
        vtkMath::Normalize(cameraUpDir);

        // Caculate the camera right direction.
        double cameraRightDir[3]{ 0., 0., 0. };
        vtkMath::Cross(cameraUpDir, cameraDir, cameraRightDir);
        vtkMath::Normalize(cameraRightDir);

        // Get the new up direction.
        double newUp[3]{ 0., 0., 0. };
        vtkMath::Cross(cameraDir, cameraRightDir, newUp);
        vtkMath::Normalize(newUp);

        // Create the matrix 3x3 for direction of image data.( Right hand )
        vtkSmartPointer<vtkMatrix3x3> mat33 = vtkSmartPointer<vtkMatrix3x3>::New();

        for (int i = 0; i < 3; i++)
        {
            mat33->SetElement(i, 0, cameraRightDir[i]);
            mat33->SetElement(i, 1, newUp[i]);
            mat33->SetElement(i, 2, cameraDir[i]);
        }

        imageData->SetDirectionMatrix(mat33);
        //@}

        // Change the size ( spacing ) of the image.
        //@{
        // Get the window size and change the position of the picture.
        int* ws = renderWindow->GetSize();
        int ww = ws[0];
        if (qFuzzyCompare(ww, 0.))
        {
            return;
        }

        // Get the camera position.
        double* cPos = camera->GetPosition();
        double dist = sqrt(vtkMath::Distance2BetweenPoints(cPos, m_pos3));

        // Get the fixed pixel width as the pixel size.
        double pixelWidth = FITKAcousticsVTKCommons::s_pictureSize.first;

        // Calculate the scale factor.
        int* dims = imageData->GetDimensions();
        double dimX = dims[0];
        if (qFuzzyCompare(dimX, 0.))
        {
            return;
        }

        double scaleFactor = pixelWidth / ww * dist / dimX;

        // Set the spaing to the image data.
        imageData->SetSpacing(scaleFactor, scaleFactor, 1.);
        //@}

        // Update the picture position.
        m_fActorImg->SetPosition(m_pos3);
    }

    void FITKAcousticsVTKGraphObjectPictureBase::setPickMode(FITKAcousticsVTKCommons::ShapePickMode mode)
    {
        if (!m_fActorImg)
        {
            return;
        }

        switch (mode)
        {
        case Exchange::FITKAcousticsVTKCommons::PickAll:
            m_fActorImg->SetPickable(true);
            break;
        case Exchange::FITKAcousticsVTKCommons::PickNone:
        case Exchange::FITKAcousticsVTKCommons::PickVertex:
        case Exchange::FITKAcousticsVTKCommons::PickEdge:
        case Exchange::FITKAcousticsVTKCommons::PickFace:
        case Exchange::FITKAcousticsVTKCommons::PickSolid:
        case Exchange::FITKAcousticsVTKCommons::PickNode:
        case Exchange::FITKAcousticsVTKCommons::PickElement:
        default:
            m_fActorImg->SetPickable(false);
            break;
        }
    }

    void FITKAcousticsVTKGraphObjectPictureBase::generateGraph()
    {
        // Create the image actor.
        m_fActorImg = FITKGraphActorImage::New();
        m_fActorImg->setGraphObject(this);
        m_fActorImg->SetPickable(false);
        m_fActorImg->SetVisibility(true);

        // Default enabled none-highlight.
        m_fActorImg->SetInputData(m_picData);

        addActor(m_fActorImg);
    }

    bool FITKAcousticsVTKGraphObjectPictureBase::getDataEnabled()
    {
        return true;
    }
}   // namespace Exchange